--- id: TASK-035 title: Copy rich text to clipboard (Ctrl+R) status: "\U0001F7E2 In progress" assignee: [] created_date: '2026-07-01 02:02' updated_date: '2026-07-01 02:54' labels: - feature dependencies: [] priority: medium ordinal: 34000 --- ## Description User-validated need: selecting rendered preview text with the mouse (TASK-034, done) and Cmd+C now works mechanically, but the pasted result is ugly — glamour's terminal rendering includes box-drawing chars, ANSI padding, and other terminal-only artifacts, not clean formatted text. A dedicated 'copy rich text' action is needed instead: convert the buffer's markdown to real formatted content (bold/headings/lists/links) and put it on the system clipboard, so pasting into Mail/Word/Docs/Pages/Slack shows proper formatting. Reuse the existing internal/export HTML pipeline (Document()/renderBody(), same as Ctrl+E/-e, TASK-030/021) as the source of truth for formatting, then convert HTML->RTF. On macOS: shell out to 'textutil -convert rtf' for the HTML->RTF conversion, then use osascript to set the pasteboard with BOTH flavors at once — «class RTF» (rich) and string (the raw markdown as plain-text fallback) — via 'set the clipboard to {«class RTF»:rtfData, string:plainText}'. Setting both flavors matters: apps that support rich paste (Mail, Word, Pages, Slack) pick the RTF automatically, while anything that doesn't falls back to plain text instead of getting nothing or gibberish. Plain atotto/clipboard.WriteAll (already used for Ctrl+C/V) only ever sets the plain-text flavor, so it can't do this alone. Non-macOS: return a clear 'unsupported' error rather than silently writing plain text only, matching the earlier design note from the archived RTF-export task. Trigger: new in-app keybind Ctrl+R (unused — current bindings are C/D/E/F/G/K/L/N/P/Q/S/T/U/V/W/X/Y/Z/Q, per internal/help/help.go), plus a headless CLI flag (-R / --copy-rich ) mirroring the -e/--export pattern (TASK-030) for scripting (Raycast/Alfred/automation). ## Acceptance Criteria - [x] #1 New function in internal/export converts already-rendered HTML to RTF bytes on darwin (shells to textutil) - [ ] #2 Clipboard receives BOTH public.rtf and plain-text flavors in one write, verified manually: pasting into TextEdit/Mail/Word shows real formatting (bold/headings/lists), pasting into a plain-text field shows the raw markdown fallback - [ ] #3 New Ctrl+R keybind (editor and preview mode) copies the current buffer as rich text, status bar confirms or reports the error - [x] #4 New -R/--copy-rich CLI flag performs the same conversion headlessly and exits, mirroring -e's UX - [ ] #5 Non-macOS platforms return a clear 'unsupported' error rather than silently writing plain text only - [x] #6 help.go and CLI usage text document the new keybind/flag - [x] #7 Tests cover the HTML->RTF conversion function and CLI/keybind wiring; the actual pasteboard write is skipped/guarded in tests (darwin-only, side-effecting) ## Implementation Notes Implemented: internal/export/rtf.go (CopyRichText/htmlToRTF/setClipboardRichText), Ctrl+R keybind + copyRichText() in app.go, -R/--copy-rich CLI flag in main.go, help.go docs. Real bug found+fixed during manual verification: AppleScript's classic 'set the clipboard to {«class RTF»:x, string:y}' record form LOOKED right in 'clipboard info' but real readers (pbpaste, a second-process NSPasteboard read) only ever got the plain-text flavor back -- rewrote setClipboardRichText to use JXA (osascript -l JavaScript) calling NSPasteboard.setDataForType directly with public.rtf / public.utf8-plain-text, which verified correctly via direct NSPasteboard read-back (byte-exact RTF content, correct plain fallback). Note: pbpaste -Prefer rtf itself is unreliable on this macOS version (always prefers plain text if present, contradicting its own man page) -- not a signal to trust; verify via a real GUI paste instead. AC#1,4,6,7 verified by code+tests. AC#2's clipboard-flavor mechanics verified headlessly (NSPasteboard read-back matches source RTF byte-for-byte); the visual 'paste into TextEdit/Mail/Word looks right' half and AC#3's in-app Ctrl+R still need a human to actually press the key and paste somewhere -- can't drive that from here. AC#5 (non-macOS error) is a one-line runtime.GOOS check, untested per-branch (same convention as the existing browserCommand GOOS-switch).